<template>
  <div>
    <!-- 顶部面包屑 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>权限管理</el-breadcrumb-item>
      <el-breadcrumb-item>角色列表</el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 中心卡片区域 -->
    <el-card class="box-card">
      <!-- 头部添加角色 -->
      <el-row>
        <el-col>
          <el-button type="primary" @click="addRole">添加角色</el-button>
        </el-col>
      </el-row>
      <!-- 角色列表 -->
      <el-table :data="rolesData" border v-loading="loading">
        <!-- 展开列 -->
        <el-table-column type="expand" v-slot="slot">
          <!-- 定义作用域插槽 -->
          <template @click="expandRow(slot.row)">
            <el-row
              :gutter="20"
              v-for="(item1,i1) in slot.row.children"
              :key="item1.id"
              :class="['bdbottom',i1===0?'bdtop':'']"
            >
              <!-- 一级菜单 -->
              <el-col :span="5">
                <el-tag closable>{{item1.authName}}</el-tag>
                <i class="el-icon-caret-right"></i>
              </el-col>
              <!-- 二级和三级菜单 -->
              <el-col :span="19">
                <!-- 通过for循环渲染二级权限 -->
                <el-row
                  v-for="(item2,i2) in item1.children"
                  :key="item2.id"
                  :class="[i2===0 ? '':'bdtop']"
                >
                  <el-col :span="6">
                    <el-tag closable type="success">{{item2.authName}}</el-tag>
                    <i class="el-icon-caret-right"></i>
                  </el-col>
                  <el-col :span="18">
                    <el-tag
                      closable
                      type="warning"
                      v-for="(item3) in item2.children"
                      :key="item3.id"
                      @close="removeTagById(slot.row,item3.id)"
                    >{{item3.authName}}</el-tag>
                  </el-col>
                </el-row>
              </el-col>
            </el-row>
          </template>
        </el-table-column>
        <el-table-column type="index" label="#"></el-table-column>
        <el-table-column prop="roleName" label="角色名称"></el-table-column>
        <el-table-column prop="roleDesc" label="角色描述"></el-table-column>
        <el-table-column v-slot="slot" label="操作" width="350">
          <el-button type="primary" icon="el-icon-edit" @click="updateRole(slot.row)" size="mini">编辑</el-button>
          <el-button
            type="danger"
            icon="el-icon-delete"
            size="mini"
            @click="deleteRoleById(slot.row)"
          >删除</el-button>
          <el-button
            type="warning"
            icon="el-icon-setting"
            size="mini"
            @click="assignRole(slot.row)"
          >分配权限</el-button>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- 添加编辑弹框 -->
    <el-dialog :title="addUpdateTitle" :visible.sync="addUpdateVisible">
      <el-form
        :model="addUpdateForm"
        :rules="addUpdateRules"
        ref="addUpdateForm"
        label-width="100px"
        class="demo-ruleForm"
      >
        <el-form-item label="角色名称" prop="roleName">
          <el-input v-model="addUpdateForm.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色描述" prop="roleDesc">
          <el-input v-model="addUpdateForm.roleDesc"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="cancelSubmit">取 消</el-button>
        <el-button type="primary" @click="addUpdateSubmit">确 定</el-button>
      </span>
    </el-dialog>

    <!-- 分配角色树状弹框 -->
    <el-dialog title="提示" @close="cancelTreeSubmit" :visible.sync="assignRoleVisible">
      <!-- 树形控件 -->
      <el-tree
        :data="rightTreeData"
        :props="defaultProps"
        show-checkbox
        default-expand-all
        :default-checked-keys="defaultKeys"
        node-key="id"
        ref="treeRef"
      ></el-tree>
      <span slot="footer" class="dialog-footer">
        <el-button @click="assignRoleVisible = false">取 消</el-button>
        <el-button type="primary" @click="treeSubmit">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { reqRoleList, reqAddRole, reqUpdateRole, reqDeleteRole, reqPermissionTree, reqDeleteRoleById, reqUpdateRoleAuth } from "../../api/index";
export default {
  data () {
    return {
      loading: false,
      // 角色列表数据
      rolesData: [],
      //   添加或修改的头部标题
      addUpdateTitle: "",
      //   弹框的显示和隐藏
      addUpdateVisible: false,
      //   表单项
      addUpdateForm: {
        roleName: '',
        roleDesc: ''
      },
      //   修改按钮当前的角色对象
      roleInfo: {},
      //  分配角色弹框的显示和隐藏
      assignRoleVisible: false,

      // 表单验证规则
      addUpdateRules: {
        roleName: [
          { required: true, message: "请输入角色名称", trigger: "blur" }
        ],
        roleDesc: [
          { required: true, message: "请输入角色描述", trigger: "blur" }
        ]
      },

      // 节点树渲染的数据源
      rightTreeData: [],
      //   节点树渲染到界面的内容
      defaultProps: {
        children: 'children',
        label: 'authName'
      },
      // 节点树显示默认的数据[第三级id数组]
      defaultKeys: [],

      //  分配角色的id
      assignRoleId: ''
    };
  },
  methods: {
    // 获取角色列表
    async getRolesList () {
      this.loading = true;
      const result = await reqRoleList()
      if (result.meta.status === 200) {
        this.rolesData = result.data
        console.log(this.rolesData)
      } else {
        this.$message.error(result.meta.msg)
      }
      this.loading = false;
    },

    async getRightTree () {
      this.loading = true
      const result = await reqPermissionTree()
      if (result.meta.status === 200) {
        this.rightTreeData = result.data
      } else {
        this.$message.error(result.meta.msg)
      }
      this.loading = false

    },

    // 添加角色
    addRole () {
      this.addUpdateVisible = true
      this.addUpdateTitle = "添加角色",
        this.addUpdateForm = {
          roleName: '',
          roleDesc: ''
        }
    },

    // 编辑角色
    updateRole (roleInfo) {
      this.addUpdateVisible = true
      this.addUpdateTitle = "修改角色",
        this.addUpdateForm = {
          roleName: roleInfo.roleName,
          roleDesc: roleInfo.roleDesc
        }
      this.roleInfo = roleInfo
      console.log(this.roleInfo)
    },

    // 删除角色
    deleteRoleById (roleInfo) {
      this.$confirm(`此操作将永久删除 ${roleInfo.roleName} 角色, 是否继续?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        const { id } = roleInfo
        console.log(roleInfo, id)
        const result = await reqDeleteRole(id)
        if (result.meta.status === 200) {
          this.$message.success('删除角色成功')
          this.getRolesList()
        } else {
          this.$message.error(result.meta.msg)
        }
      }).catch(err => err)
    },

    // 取消表单提交
    cancelSubmit () {
      this.addUpdateVisible = false
      this.$refs.addUpdateForm.resetFields()
    },

    // 表单提交的验证
    addUpdateSubmit () {
      this.$refs.addUpdateForm.validate(async valid => {
        if (!valid) return false
        if (!this.roleInfo.id) {
          const { roleName, roleDesc } = this.addUpdateForm
          const result = await reqAddRole(roleName, roleDesc)
          if (result.meta.status === 201) {
            this.$message.success('添加角色成功')
            this.getRolesList()
          } else {
            this.$message.error(result.meta.msg)
          }
          // 弹框消失
          this.addUpdateVisible = false
        } else {
          const { roleName, roleDesc } = this.addUpdateForm
          const result = await reqUpdateRole(this.roleInfo.id, roleName, roleDesc)
          if (result.meta.status === 200) {
            this.$message.success('修改角色成功')
            this.getRolesList()
          } else {
            this.$message.error(result.meta.msg)
          }
          // 弹框消失
          this.addUpdateVisible = false
        }
      })
    },


    // 分配角色
    assignRole (roleInfo) {
      this.assignRoleId = roleInfo.id
      this.getRightTree()
      this.assignRoleVisible = true
      // 递归获取三级节点的id
      this.getLastNodeIds(roleInfo, this.defaultKeys)
    },

    // 通过递归的形式 获取三级列表的id
    getLastNodeIds (treeNode, arr) {
      // 如果节点没有children，证明是三级列表
      if (!treeNode.children) {
        return arr.push(treeNode.id)
      }

      // 如果有children，继续遍历然后进行递归
      treeNode.children.forEach(item => this.getLastNodeIds(item, arr))
    },


    // 取消树形权限的提交
    cancelTreeSubmit () {
      // 清空上一个需要渲染的 defaultKeys
      this.defaultKeys = []
    },

    // 点击确认更新权限
    async treeSubmit () {
      this.assignRoleVisible = false
      // 获取树形结构的引用，调用其半选中和全选中的节点
      const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys()
      ]

      // 以逗号的形式分割传给后台[后台要求]
      const idStr = keys.join(',')

      // 发送请求
      const result = await reqUpdateRoleAuth(this.assignRoleId, idStr)
      if (result.meta.status === 200) {
        this.$message.success('更新权限成功')
        this.getRolesList()
      } else {
        this.$message.error(result.meta.msg)
      }
    },

    // 点击删除tag标签
    removeTagById (roleInfo, rightId) {
      this.$confirm('您将删除该权限?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        const result = await reqDeleteRoleById(roleInfo.id, rightId)
        if (result.meta.status === 200) {
          this.$message.success('删除成功')
          // 删除完成但不想重新刷新页面，只想更新数据
          roleInfo.children = result.data
        } else {
          this.$message.error(result.meta.msg)
        }
      }).catch(err => err)
    }
  },
  created () {
    this.getRolesList()
  }
};
</script>

<style lang="less" scoped>
.el-card {
  margin-top: 20px;
  width: 100%;
  height: 100%;

  .el-table {
    width: 98%;
    height: 100%;
    margin-top: 20px;
  }

  .el-tag {
    margin: 7px;
  }

  .el-row {
    display: flex;
    align-items: center;
  }

  .bdtop {
    border-top: 1px solid #ccc;
  }
  .bdbottom {
    border-bottom: 1px solid #ccc;
  }
}
/**
难点一.展开列定义作用域插槽 需要使用template

 <!-- 展开列 -->
<el-table-column type="expand" v-slot="slot">
    <!-- 定义作用域插槽 -->
    <template   @click="expandRow(slot.row)">
        <pre>
            {{slot.row}}
        </pre>
    </template>
</el-table-column>



难点二：tag页面的排版
<el-table-column type="expand" v-slot="slot">
    <!-- 定义作用域插槽 -->
    <template  @click="expandRow(slot.row)">
        <el-row :gutter="20" v-for="(item1,i1) in slot.row.children" :key="item1.id"  :class="['bdbottom',i1===0?'bdtop':'']">
            <!-- 一级菜单 -->
            <el-col :span="5">
                <el-tag closable>{{item1.authName}}</el-tag>
                <i class="el-icon-caret-right"></i>
            </el-col>
            <!-- 二级和三级菜单 -->
            <el-col :span="19">
                <!-- 通过for循环渲染二级权限 -->
                <el-row  v-for="(item2,i2) in item1.children" :key="item2.id" :class="[i2===0 ? '':'bdtop']">
                    <el-col :span="6">
                        <el-tag closable type="success" >{{item2.authName}}</el-tag>
                        <i class="el-icon-caret-right"></i>
                    </el-col>
                    <el-col :span="18">
                            <el-tag closable type="warning" v-for="(item3) in item2.children" :key="item3.id" @close="removeTagById(slot.row,item3.id)">{{item3.authName}}</el-tag>
                    </el-col>
                </el-row>
            </el-col>
        </el-row>
    </template>
</el-table-column>




难点三：删除tag标签但不想重新刷新页面，只想更新数据
点击删除tag标签
    removeTagById(roleInfo,rightId){
       this.$confirm('您将删除该权限?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(async () => {
          const result = await reqDeleteRoleById(roleInfo.id,rightId)
            if(result.meta.status === 200){
                this.$message.success('删除成功')
                // 删除完成但不想重新刷新页面，只想更新数据
                roleInfo.children = result.data
            }else{
                this.$message.error(result.meta.msg)
            }
        }).catch(err => err)
    }


难点四  树形结构
难点逐个解析
①:data="data" ------>:data 数据绑定，为整棵tree提供数据源
<el-tree :data="rightTreeData"></el-tree>
data() {
    return {
    节点树渲染的数据源
      data:[],
}

②:props="defaultProps"  :props 为tree绑定显示的字段【文本，上下级的嵌套】，
defaultProps 中的 children，实现父子嵌套使用的属性；label 显示的文本
 defaultProps: {
    children: 'children',
    label: 'label'
}
<el-tree :data="rightTreeData" :props="defaultProps"></el-tree>
 data() {
    return {
    节点树渲染到界面的内容
      defaultProps: {
         children: 'children',
         label: 'authName'
      },
}


③show-checkbox  是否出现复选框
<el-tree :data="rightTreeData" :props="defaultProps" show-checkbox></el-tree>


④node-key="id"  选中复选框的时候需要获取到选中的id值
<el-tree :data="rightTreeData" :props="defaultProps" show-checkbox default-expand-all node-key="id"></el-tree>


⑤default-expand-all  默认展开所有的节点
<el-tree :data="rightTreeData" :props="defaultProps" show-checkbox node-key="id" default-expand-all></el-tree>


⑥default-checked-keys="默认选中id值的数组【注：只需获取最后一级的子id值，父会根据子来显示全选或半选】"  点击分配权限按钮的时候获取数据 将默认已有的权限显示在 tree 中
因为只需要获取最后一级节点的id，可以通过递归的形式方法来获取
<el-tree :data="rightTreeData" :props="defaultProps" show-checkbox default-expand-all :default-checked-keys="defaultKeys" node-key="id"></el-tree>

data(){
    return(){
        defaultKeys:[]
    }
}


methods:{
    分配角色
    assignRole(roleInfo){
        this.defaultKeys = []
        this.getRightTree()
        this.assignRoleVisible = true
        console.log(roleInfo)
        // 递归获取三级节点的id
        this.getLastNodeIds(roleInfo, this.defaultKeys)
    },

    通过递归的形式 获取三级列表的id
    getLastNodeIds(treeNode, arr){
        // 如果节点没有children，证明是三级列表
        if(!treeNode.children){
            return  arr.push(treeNode.id)
        }

        // 如果有children，继续遍历然后进行递归
        treeNode.children.forEach(item=> this.getLastNodeIds(item,arr))
    },
}


出现的bug：点击A分配角色按钮，后再点击B角色按钮，B中没有的权限也出现了
解决办法：点击关闭的时候清空id数组  @close="cancelTreeSubmit"
<el-dialog
    title="提示"
    @close="cancelTreeSubmit"
    :visible.sync="assignRoleVisible"
    >

methods:{
    cancelTreeSubmit(){
        // 清空上一个需要渲染的 defaultKeys
        this.defaultKeys = []
    },
}

⑦获取全选和半全选的输入框的id
getCheckedKeys 获取所有已选中的值组成的数组
getHalfCheckedKeys  获取所有半选中的值组成的数组

1. 给 tree 添加 ref="treeRef"
<el-tree :data="rightTreeData" :props="defaultProps" show-checkbox default-expand-all :default-checked-keys="defaultKeys" node-key="id" ref="treeRef"></el-tree>

2. 给按钮绑定事件 @click="treeSubmit"
<el-button type="primary" @click="treeSubmit">确 定</el-button>

3. 获取半选中和全选中的节点
async treeSubmit(){
    this.assignRoleVisible = false
    // 获取树形结构的引用，调用其半选中和全选中的节点
    const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys()
    ]
    console.log(keys)
    },

4. 将keys分割
async treeSubmit(){
    this.assignRoleVisible = false
    // 获取树形结构的引用，调用其半选中和全选中的节点
    const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys()
    ]

    // 以逗号的形式分割传给后台[后台要求]
    const idStr = keys.join(',')

    console.log(idStr)
    },

5. 发送请求
async treeSubmit(){
    this.assignRoleVisible = false
    // 获取树形结构的引用，调用其半选中和全选中的节点
    const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys()
    ]

    // 以逗号的形式分割传给后台[后台要求]
    const idStr = keys.join(',')

    // 发送请求
    const result = await reqUpdateRoleAuth(this.assignRoleId,idStr)
    if (result.meta.status === 200) {
        this.$message.success('更新权限成功')
        this.getRolesList()
        } else {
            this.$message.error(result.meta.msg)
        }
    },

*/
</style>
